package com.onabilityresult;

import java.util.LinkedHashMap;
import java.util.Map;

import com.onabilityresult.internal.IOnAbilityResult;
import ohos.aafwk.content.Intent;

public final class AbilityResult {
    private static final String ABILITY_RESULT_CLASS_SUFFIX = "$$OnAbilityResult";

    static final Map<Class<?>, IOnAbilityResult<Object>> ON_ABILITY_RESULTS = new LinkedHashMap<>();

    static IOnAbilityResult<Object> createOnAbilityResultClassFor(final Object object) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        final IOnAbilityResult<Object> abilityResultForClass = findAbilityResultForClass(object.getClass());

        if (abilityResultForClass == null) {
            throw new ClassNotFoundException();
        }

        return abilityResultForClass;
    }

    private static IOnAbilityResult<Object> findAbilityResultForClass(final Class<?> clazz) throws IllegalAccessException, InstantiationException {
        final IOnAbilityResult<Object> cachedOnAbilityResult = ON_ABILITY_RESULTS.get(clazz);

        if (cachedOnAbilityResult != null) {
            return cachedOnAbilityResult;
        }

        final String className = clazz.getName();

        if (className.startsWith("ohos.") || className.startsWith("java.")) {
            return null;
        }

        IOnAbilityResult<Object> onAbilityResult;

        try {
            final Class<?> onAbilityResultClass = clazz.getClassLoader().loadClass(className + ABILITY_RESULT_CLASS_SUFFIX);
            // noinspection unchecked
            onAbilityResult = (IOnAbilityResult<Object>) onAbilityResultClass.newInstance();
        } catch (final ClassNotFoundException ignore) {
            onAbilityResult = findAbilityResultForClass(clazz.getSuperclass());
        }

        ON_ABILITY_RESULTS.put(clazz, onAbilityResult);

        return onAbilityResult;
    }

    public static OnResult onResult(final int requestCode, final int resultCode, final Intent intent) {
        return new OnResult(requestCode, resultCode, intent);
    }

    private AbilityResult() {
        throw new AssertionError("No instances.");
    }

    public static final class OnResult {
        private final int              mRequestCode;
        private final int              mResultCode;
        private final Intent mIntent;

        OnResult(final int requestCode, final int resultCode, final Intent intent) {
            mRequestCode = requestCode;
            mResultCode = resultCode;
            mIntent = intent;
        }

        public <T> boolean into(final T object) {
            final IOnAbilityResult<Object> onAbilityResult;

            try {
                onAbilityResult = AbilityResult.createOnAbilityResultClassFor(object);
            } catch (final ClassNotFoundException classNotFound) {
                throw new AbilityResultRuntimeException("Could not find OnAbilityResult class for " + object.getClass().getName(), classNotFound);
            } catch (final IllegalAccessException illegalAccessException) {
                throw new AbilityResultRuntimeException("Can't create OnAbilityResult class for " + object.getClass().getName(), illegalAccessException);
            } catch (final InstantiationException instantiationException) {
                throw new AbilityResultRuntimeException("Exception when handling IOnAbilityResult " + instantiationException.getMessage(), instantiationException);
            }

            return onAbilityResult.onResult(object, mRequestCode, mResultCode, mIntent);
        }
    }

    public static class AbilityResultRuntimeException extends RuntimeException {
        public AbilityResultRuntimeException(final String detailMessage, final Throwable throwable) {
            super(detailMessage, throwable);
        }
    }
}
